#pragma once
#include "Runtime/Math/Color.h"
#include "Runtime/Misc/TypeHash.h"

namespace Alice
{
    enum class EClearBinding
	{
		ENoneBound, //no clear color associated with this target.  Target will not do hardware clears on most platforms
		EColorBound, //target has a clear color bound.  Clears will use the bound color, and do hardware clears.
		EDepthStencilBound, //target has a depthstencil value bound.  Clears will use the bound values and do hardware clears.
	};
    struct FClearValueBinding
	{
		struct DSVAlue
		{
			float Depth;
			uint32 Stencil;
		};
    	EClearBinding ColorBinding;
    	union ClearValueType
    	{
    		float Color[4];
    		DSVAlue DSValue;
    	} Value;
    	// common clear values
    	static const FClearValueBinding None;
    	static const FClearValueBinding Black;
    	static const FClearValueBinding BlackMaxAlpha;
    	static const FClearValueBinding White;
    	static const FClearValueBinding Transparent;
    	static const FClearValueBinding DepthOne;
    	static const FClearValueBinding DepthZero;
    	static const FClearValueBinding DepthNear;
    	static const FClearValueBinding DepthFar;	
    	static const FClearValueBinding Green;
    	static const FClearValueBinding DefaultNormal8Bit;
		FClearValueBinding() : ColorBinding(EClearBinding::EColorBound)
		{
			Value.Color[0] = 0.0f;
			Value.Color[1] = 0.0f;
			Value.Color[2] = 0.0f;
			Value.Color[3] = 0.0f;
		}

		FClearValueBinding(EClearBinding NoBinding)
			: ColorBinding(NoBinding)
		{
			Value.Color[0] = 0.0f;
			Value.Color[1] = 0.0f;
			Value.Color[2] = 0.0f;
			Value.Color[3] = 0.0f;

			Value.DSValue.Depth = 0.0f;
			Value.DSValue.Stencil = 0;
		}

		explicit FClearValueBinding(const FLinearColor& InClearColor): ColorBinding(EClearBinding::EColorBound)
		{
			Value.Color[0] = InClearColor.R;
			Value.Color[1] = InClearColor.G;
			Value.Color[2] = InClearColor.B;
			Value.Color[3] = InClearColor.A;
		}
		explicit FClearValueBinding(float DepthClearValue, uint32 StencilClearValue = 0): ColorBinding(EClearBinding::EDepthStencilBound)
		{
			Value.DSValue.Depth = DepthClearValue;
			Value.DSValue.Stencil = StencilClearValue;
		}

		FLinearColor GetClearColor() const
		{
			return FLinearColor(Value.Color[0], Value.Color[1], Value.Color[2], Value.Color[3]);
		}

		void GetDepthStencil(float& OutDepth, uint32& OutStencil) const
		{
			OutDepth = Value.DSValue.Depth;
			OutStencil = Value.DSValue.Stencil;
		}

		bool operator==(const FClearValueBinding& Other) const
		{
			if (ColorBinding == Other.ColorBinding)
			{
				if (ColorBinding == EClearBinding::EColorBound)
				{
					return
						Value.Color[0] == Other.Value.Color[0] &&
						Value.Color[1] == Other.Value.Color[1] &&
						Value.Color[2] == Other.Value.Color[2] &&
						Value.Color[3] == Other.Value.Color[3];

				}
				if (ColorBinding == EClearBinding::EDepthStencilBound)
				{
					return
						Value.DSValue.Depth == Other.Value.DSValue.Depth &&
						Value.DSValue.Stencil == Other.Value.DSValue.Stencil;
				}
				return true;
			}
			return false;
		}
	};
	inline uint32 GetTypeHash(FClearValueBinding const& Binding)
	{
		uint32 Hash = GetTypeHash(Binding.ColorBinding);
		if (Binding.ColorBinding == EClearBinding::EColorBound)
		{
			Hash = HashCombine(Hash, ::GetTypeHash(Binding.Value.Color[0]));
			Hash = HashCombine(Hash, ::GetTypeHash(Binding.Value.Color[1]));
			Hash = HashCombine(Hash, ::GetTypeHash(Binding.Value.Color[2]));
			Hash = HashCombine(Hash, ::GetTypeHash(Binding.Value.Color[3]));
		}
		else if (Binding.ColorBinding == EClearBinding::EDepthStencilBound)
		{
			Hash = HashCombine(Hash, ::GetTypeHash(Binding.Value.DSValue.Depth));
			Hash = HashCombine(Hash, ::GetTypeHash(Binding.Value.DSValue.Stencil));
		}

		return Hash;
	}
}
